using System;
using System.Text;
using System.Collections.Generic;

namespace Dida.Dxml
{
	/// <summary>
	/// This class specifies a reference during the compilation process.
	/// </summary>
	public class CompilingReference
	{
		string name;
		List<string> nameParts;
		bool isAbsolute;
		Type type;

		XmlPositionMarker errorPos;

		public CompilingReference(string name,Type type, XmlPositionMarker errorPos)
		{
			this.name = name;
			this.type = type;
			this.errorPos = errorPos;

			ParseNameParts(name, errorPos);

			isAbsolute = name[0] == '.';
		}

		private void ParseNameParts(string name, XmlPositionMarker errorPos)
		{
			nameParts = new List<string>();
			StringBuilder curPart = new StringBuilder();
			bool dotAllowed = true;
			bool isFirstChar = true;
			for (int i = 0; i < name.Length; i++)
			{
				if (name[i] == '.')
				{
					if (!dotAllowed)
						throw new XmlReaderException("Unexpected dot in name.", errorPos);

					if (!isFirstChar)
					{
						ValidateNamePart(curPart.ToString(), errorPos);
						nameParts.Add(curPart.ToString());
					}

					dotAllowed = false;
					curPart = new StringBuilder();
				}
				else
				{
					curPart.Append(name[i]);
					dotAllowed = true;
				}

				isFirstChar = false;
			}

			if (curPart.Length > 0)
			{
				ValidateNamePart(curPart.ToString(), errorPos);
				nameParts.Add(curPart.ToString());
			}
		}

		private void ValidateNamePart(string namePart, XmlPositionMarker errorPos)
		{
			for (int i = 0; i < namePart.Length; i++)
			{
				if (namePart[i] == ' ' || namePart[i] == '\t')
					throw new XmlReaderException("A name shouldn't contain whitespace.", errorPos);

				if (!((namePart[i] >= 'A' && namePart[i] <= 'Z') ||
					(namePart[i] >= 'a' && namePart[i] <= 'z') ||
					(namePart[i] >= '0' && namePart[i] <= '9') ||
					namePart[i] == '_' || namePart[i] == '.'))
				{
					throw new XmlReaderException("Invalid token in name: \'" + namePart[i] + "\'.", errorPos);
				}
			}
		}

		public static CompilingReference CreateAbsoluteReference(string name,Type type, XmlPositionMarker errorPos)
		{
			CompilingReference ret = new CompilingReference(name, type,errorPos);
			ret.isAbsolute = true;
			return ret;
		}

		public string Name
		{
			get { return name; }
		}

		public IList<string> NameParts
		{
			get { return nameParts; }
		}

		public bool IsAbsolute
		{
			get { return isAbsolute; }
		}

		/// <summary>
		/// The type of object this is a reference to. 
		/// </summary>
		/// <remarks>
		/// The object this is a reference to must be compatible with 
		/// this type (see <see cref = "Type.IsAssignableFrom"/>).
		/// </remarks>
		public Type ReferencedType
		{
			get { return type; }
		}

		public XmlPositionMarker ErrorPosition
		{
			get { return errorPos; }
		}
	}
}